<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="nj84D" id="nj84D"><span data-lake-id="u80beb1b2" id="u80beb1b2">典型回答</span></h1>
  <p data-lake-id="ubd690087" id="ubd690087"><br></p>
  <p data-lake-id="u2e4cf1de" id="u2e4cf1de"><br></p>
  <p data-lake-id="u4518e6df" id="u4518e6df"><span data-lake-id="u05553903" id="u05553903">上文介绍了如何确保RabbitMQ的发送者把消息能够投递给RabbitMQ的Exchange和Queue，那么，Queue又是如何保证消息能不丢的呢？</span></p>
  <p data-lake-id="u405cb496" id="u405cb496"><span data-lake-id="u41d125b3" id="u41d125b3">​</span><br></p>
  <p data-lake-id="u87859703" id="u87859703"><span data-lake-id="ua55fd958" id="ua55fd958">RabbitMQ在接收到消息后，默认并不会立即进行持久化，而是先把消息暂存在内存中，这时候如果MQ挂了，那么消息就会丢失。所以需要通过持久化机制来保证消息可以被持久化下来。</span></p>
  <p data-lake-id="u6adc5ba6" id="u6adc5ba6"><span data-lake-id="ud5856e23" id="ud5856e23">​</span><br></p>
  <h3 data-lake-id="feRow" id="feRow"><span data-lake-id="u91e9763e" id="u91e9763e">队列和交换机的持久化</span></h3>
  <p data-lake-id="u557dd13e" id="u557dd13e"><strong><span data-lake-id="u21be2fba" id="u21be2fba">​</span></strong><br></p>
  <p data-lake-id="u64d31fce" id="u64d31fce"><span data-lake-id="u349abffe" id="u349abffe">在声明队列时，可以通过设置</span><code data-lake-id="u6a589257" id="u6a589257"><span data-lake-id="ue184d398" id="ue184d398">durable</span></code><span data-lake-id="ue0718e2d" id="ue0718e2d">参数为true来创建一个持久化队列。持久化队列会在RabbitMQ服务器重启后保留，确保队列的元数据不会丢失。</span></p>
  <p data-lake-id="u22b10b84" id="u22b10b84"><br></p>
  <p data-lake-id="u82e687d5" id="u82e687d5"><span data-lake-id="u5ee17a2b" id="u5ee17a2b">在声明交换机时，也可以通过设置</span><code data-lake-id="u1c28c076" id="u1c28c076"><span data-lake-id="ua1ce2d5f" id="ua1ce2d5f">durable</span></code><span data-lake-id="u2b8a557d" id="u2b8a557d">参数为true来创建一个持久化交换机。持久化交换机会在RabbitMQ服务器重启后保留，以确保交换机的元数据不会丢失。</span></p>
  <p data-lake-id="u830bf54a" id="u830bf54a"><span data-lake-id="u00e2395c" id="u00e2395c">​</span><br></p>
  <p data-lake-id="u1f71f69c" id="u1f71f69c"><span data-lake-id="u360350bd" id="u360350bd">绑定关系通常与队列和交换机相关联。当创建绑定关系时，还是可以设置</span><code data-lake-id="uee437138" id="uee437138"><span data-lake-id="u77eb30a9" id="u77eb30a9">durable</span></code><span data-lake-id="u883395be" id="u883395be">参数为true，以创建一个持久化绑定。持久化绑定关系会在服务器重启后保留，以确保绑定关系不会丢失。</span></p>
  <p data-lake-id="u8aabe580" id="u8aabe580"><span data-lake-id="u4adc19b9" id="u4adc19b9">​</span><br></p>
  <pre lang="java"><code>
@Bean
public Queue TestQueue() {
    // 第二个参数durable:是否持久化,默认是false
    return new Queue("queue-name",true,true,false);
}


@Bean
public DirectExchange mainExchange() {
  	//第二个参数durable:是否持久化,默认是false
    return new DirectExchange("main-exchange",true,false);
}
</code></pre>
  <p data-lake-id="u9027d9db" id="u9027d9db"><br></p>
  <p data-lake-id="ub9287d89" id="ub9287d89"><br></p>
  <h3 data-lake-id="Ahjrl" id="Ahjrl"><span data-lake-id="ud3d50653" id="ud3d50653">持久化消息</span></h3>
  <p data-lake-id="u993f463c" id="u993f463c"><span data-lake-id="u02b5bb6d" id="u02b5bb6d">​</span><br></p>
  <p data-lake-id="u24a6d427" id="u24a6d427"><span data-lake-id="uca2da71c" id="uca2da71c">生产者发送的消息可以通过设置消息的</span><code data-lake-id="u534eed0b" id="u534eed0b"><span data-lake-id="uc6549152" id="uc6549152">deliveryMode</span></code><span data-lake-id="ua56c34bf" id="ua56c34bf">为2来创建持久化消息。持久化消息在发送到持久化队列后，将在服务器重启后保留，以确保消息不会丢失。</span></p>
  <p data-lake-id="u88dc0056" id="u88dc0056"><span data-lake-id="u6302a25d" id="u6302a25d">​</span><br></p>
  <blockquote data-lake-id="u07aaa787" id="u07aaa787">
   <p data-lake-id="u8291b1f3" id="u8291b1f3"><span data-lake-id="uc5b092e6" id="uc5b092e6">deliveryMode是一项用于设置消息传递模式的属性，用于指定消息的持久性级别。deliveryMode可以具有两个值：</span></p>
   <ol list="ube516b3a">
    <li fid="u79906441" data-lake-id="ue89e8aea" id="ue89e8aea" data-lake-index-type="true"><span data-lake-id="ued9dc799" id="ued9dc799">1（非持久化）</span><span data-lake-id="u4e452e67" id="u4e452e67">：这是默认的传递模式。如果消息被设置为非持久化，RabbitMQ将尽力将消息传递给消费者，但不会将其写入磁盘，这意味着如果RabbitMQ服务器在消息传递之前崩溃或重启，消息可能会丢失。</span></li>
    <li fid="u79906441" data-lake-id="uc6de21c2" id="uc6de21c2" data-lake-index-type="true"><span data-lake-id="u28c842cc" id="u28c842cc">2（持久化）：如果消息被设置为持久化，RabbitMQ会将消息写入磁盘，以确保即使在RabbitMQ服务器重启时，消息也不会丢失。持久化消息对于重要的消息非常有用，以确保它们不会在传递过程中丢失。</span></li>
   </ol>
  </blockquote>
  <p data-lake-id="ufaaef147" id="ufaaef147"><span data-lake-id="ue6c410f4" id="ue6c410f4">​</span><br></p>
  <pre lang="java"><code>
Message message = MessageBuilder.withBody("hello, spring".getBytes(StandardCharsets.UTF_8)) //kp 消息体，字符集
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  .setDeliveryMode(MessageDeliveryMode.PERSISTENT) 
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  .build();

rabbitTemplate.convertAndSend("simple.queue", message);
</code></pre>
  <p data-lake-id="uee4e67cd" id="uee4e67cd"><span data-lake-id="u7093f9ab" id="u7093f9ab">​</span><br></p>
  <p data-lake-id="u8ffc0000" id="u8ffc0000"><span data-lake-id="u9649e1a5" id="u9649e1a5">通过设置</span><code data-lake-id="uf52aa425" id="uf52aa425"><span data-lake-id="u662d0c09" id="u662d0c09">deliveryMode</span></code><span data-lake-id="uf4233087" id="uf4233087">类实现消息的持久化。但是需要注意，将消息设置为持久化会增加磁盘I/O开销。</span></p>
  <p data-lake-id="ue6ede8bc" id="ue6ede8bc"><span data-lake-id="uff76c607" id="uff76c607">​</span><br></p>
  <h3 data-lake-id="Eqwm5" id="Eqwm5"><span data-lake-id="u095f8a42" id="u095f8a42">消费者确认机制</span></h3>
  <p data-lake-id="u5d6b744a" id="u5d6b744a"><strong><span data-lake-id="u24ccb468" id="u24ccb468">​</span></strong><br></p>
  <p data-lake-id="ucb01b97c" id="ucb01b97c"><span data-lake-id="u72075e56" id="u72075e56">有了持久化机制后，那么怎么保证消息在持久化下来之后一定能被消费者消费呢？这里就涉及到消息的消费确认机制。</span></p>
  <p data-lake-id="ud6f16d93" id="ud6f16d93"><span data-lake-id="u8eed9568" id="u8eed9568">​</span><br></p>
  <p data-lake-id="u6b378de4" id="u6b378de4"><span data-lake-id="u195f9978" id="u195f9978">在RabbitMQ中，消费者处理消息成功后可以向MQ发送ack回执，MQ收到ack回执后才会删除该消息，这样才能确保消息不会丢失。如果消费者在处理消息中出现了异常，那么就会返回nack回执，MQ收到回执之后就会重新投递一次消息，如果消费者一直都没有返回ACK/NACK的话，那么他也会在尝试重新投递。</span></p>
  <p data-lake-id="u26bc29f8" id="u26bc29f8"><span data-lake-id="u94a37551" id="u94a37551">​</span><br></p>
  <h3 data-lake-id="OwBhF" id="OwBhF"><span data-lake-id="uab11ce2d" id="uab11ce2d">无法做到100%不丢</span></h3>
  <p data-lake-id="u89675d0c" id="u89675d0c"><strong><span data-lake-id="ucd130702" id="ucd130702">​</span></strong><br></p>
  <p data-lake-id="u966559eb" id="u966559eb"><span data-lake-id="ue67d7215" id="ue67d7215">虽然我们通过发送者端进行异步回调、MQ进行持久化、消费者做确认机制，但是也没办法保证100%不丢，因为MQ的持久化过程其实是异步的。即使我们开了持久化，也有可能在内存暂存成功后，异步持久化之前宕机了，那么这个消息就会丢失。</span></p>
  <p data-lake-id="u960a270c" id="u960a270c"><span data-lake-id="uf443bdc2" id="uf443bdc2">​</span><br></p>
  <p data-lake-id="u3c0f4250" id="u3c0f4250"><span data-lake-id="u3e35f3f8" id="u3e35f3f8">如果想要做到100%不丢失，就需要引入本地消息表，来通过轮询的方式来进行消息重投。</span></p>
  <p data-lake-id="u6f8565ae" id="u6f8565ae"><span data-lake-id="u98d0690b" id="u98d0690b">​</span><br></p>
 </body>
</html>